/**
 * Copyright 2019-2029 geekidea(https://github.com/geekidea)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package generator;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器工厂类
 * @author geekidea
 * @date 2018-11-08
 */
public class Generator {

    /**
     * 实体类公共包
     */
    private static final String ENTITY = "entity";

    /**
     * 乐观锁字段
     */
    private static final String VERSION_FIELD = "version";

    /**
     * 逻辑删除字段
     */
    private static final String LOGIC_DELETE_FIELD = "delete_flag";

    /**
     * 代码生成器
     * @param tableName
     */
    public void generator(GeneratorDataSource generatorDataSource, GeneratorInfo generatorInfo, String tableName) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor(generatorInfo.getAuthor());
        gc.setSwagger2(true);
        gc.setMapperName("%sMapper");
        gc.setXmlName("%s");
        gc.setEntityName("%sPO");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        gc.setFileOverride(true);
        gc.setOpen(false);
        gc.setIdType(IdType.AUTO);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(generatorDataSource.getDriverUrl());
        dsc.setDriverName(generatorDataSource.getDriverName());
        dsc.setUsername(generatorDataSource.getUserName());
        dsc.setPassword(generatorDataSource.getPassword());
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent(generatorInfo.getProjectPackage());
        pc.setModuleName(generatorInfo.getGeneratorModule());
        pc.setEntity(tableName + StringPool.DOT + ENTITY);
        pc.setController(tableName);
        pc.setServiceImpl(tableName);
        pc.setService(tableName);
        pc.setMapper(tableName);
        pc.setXml(tableName);
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("objectName", underlineToPascal(tableName));
                map.put("objectMapperName", underlineToCamel(tableName) + "Mapper");
                map.put("objectServiceName", underlineToCamel(tableName) + "Service");
                map.put("vo", underlineToPascal(tableName) + "VO");
                map.put("updateBO", underlineToPascal(tableName) + "UpdateBO");
                map.put("updateBOVariable", underlineToCamel(tableName) + "UpdateBO");
                map.put("bo", underlineToPascal(tableName) + "BO");
                map.put("boVariable", underlineToCamel(tableName) + "BO");
                map.put("po", underlineToPascal(tableName) + "PO");
                map.put("poVariable", underlineToCamel(tableName));
                this.setMap(map);
            }
        };
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义PO模板
        focList.add(new FileOutConfig("/templates/po.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + ENTITY + "/" + underlineToPascal(tableName) + "PO" + StringPool.DOT_JAVA;
            }
        });
        // 自定义BO模板
        focList.add(new FileOutConfig("/templates/bo.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + ENTITY + "/" + underlineToPascal(tableName) + "BO" + StringPool.DOT_JAVA;
            }
        });
        // 自定义updateBO模板
        focList.add(new FileOutConfig("/templates/bo_update.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + ENTITY + "/" + underlineToPascal(tableName) + "UpdateBO" + StringPool.DOT_JAVA;
            }
        });
        // 自定义VO模板
        focList.add(new FileOutConfig("/templates/vo.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + ENTITY + "/" + underlineToPascal(tableName) + "VO" + StringPool.DOT_JAVA;
            }
        });
        // 自定义Controller模板
        focList.add(new FileOutConfig("/templates/controller.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + tableInfo.getControllerName() + StringPool.DOT_JAVA;
            }
        });
        // 自定义Service模板
        focList.add(new FileOutConfig("/templates/service.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + tableInfo.getServiceName() + StringPool.DOT_JAVA;
            }
        });
        // 自定义ServiceImp模板
        focList.add(new FileOutConfig("/templates/serviceImpl.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + tableInfo.getServiceImplName() + StringPool.DOT_JAVA;
            }
        });
        // 自定义Mapper模板
        focList.add(new FileOutConfig("/templates/mapper.java.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return projectPath + "/src/main/java/"+ generatorInfo.getProjectPackage().replace(".", "/") +"/" +
                        pc.getModuleName() + "/" + tableName + "/" + tableInfo.getMapperName() + StringPool.DOT_JAVA;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        mpg.setTemplate(new TemplateConfig().setController(null).setServiceImpl(null).setService(null).setEntity(null).setMapper(null));
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setInclude(tableName);
        strategy.setEntityLombokModel(true);
        strategy.setVersionFieldName(VERSION_FIELD);
        strategy.setLogicDeleteFieldName(LOGIC_DELETE_FIELD);
        mpg.setStrategy(strategy);
        mpg.execute();
    }

    /**
     * 首字母小写驼峰
     * @param underline
     * @return
     */
    private String underlineToCamel(String underline){
        if (StrUtil.isNotBlank(underline)){
            return NamingStrategy.underlineToCamel(underline);
        }
        return null;
    }

    /**
     * 首字母大写驼峰
     * @param underline
     * @return
     */
    private String underlineToPascal(String underline){
        if (StrUtil.isNotBlank(underline)){
            return NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel(underline));
        }
        return null;
    }
}
